use std::{
    collections::HashMap,
    io::Read,
    sync::OnceLock,
};

static PIN_YIN_MAP: OnceLock<Vec<(char, String)>> = OnceLock::new();
static PHRASE_PIN_YIN_MAP: OnceLock<Vec<(String, String)>> = OnceLock::new();
static WADE_MAP: OnceLock<HashMap<String, String>> = OnceLock::new();
static T2S_MAP: OnceLock<Vec<(char, char)>> = OnceLock::new();

fn init_t2s_map() -> Vec<(char, char)> {
    // opencc
    let mut file = std::fs::File::open("./TSCharacters.txt").unwrap();
    let mut total_s = String::with_capacity(35 * 1024);
    let mut res = Vec::new();
    file.read_to_string(&mut total_s).unwrap();
    total_s.split('\n').for_each(|l| {
        let mut n = l.split(' ');
        res.push((
            n.next().unwrap().chars().next().unwrap(),
            n.next().unwrap().chars().next().unwrap(),
        ));
    });
    res
}

fn init_large_pin_yin_map() -> Vec<(String, String)> {
    // version: 0.17.0
    // source: https://github.com/mozillazg/phrase-pinyin-data
    let mut file = std::fs::File::open("./pinyin.txt").unwrap();

    let mut res: Vec<(String, String)> = Vec::new();
    let mut total_s = String::with_capacity(2 * 1024 * 1024);
    file.read_to_string(&mut total_s).unwrap();
    total_s.split('\n').into_iter().for_each(|d| {
        let mut _t = d.split(':');
        res.push((
            _t.next().unwrap().to_string(),
            _t.next().unwrap().to_string(),
        ));
    });
    res
}

fn init_pin_yin_map() -> Vec<(char, String)> {
    let file = std::fs::File::open("./dict_full.json").unwrap();
    let reader = std::io::BufReader::new(file);

    let s: serde_json::Value = serde_json::from_reader(reader).unwrap();
    let m = s.get("dict").unwrap().as_array().unwrap();
    let mut word_2_pin_yin = Vec::with_capacity(m.len());
    for ele in m {
        let ele = ele.as_array().unwrap();
        let words = ele[1].as_str().unwrap().to_string();
        let pin_yin = ele[0].as_str().unwrap().to_string();
        for word in words.chars() {
            let tuple_pw = (word, pin_yin.clone());
            word_2_pin_yin.push(tuple_pw);
        }
    }
    word_2_pin_yin.sort_unstable();
    word_2_pin_yin
}

fn init_wade_map() -> HashMap<String, String> {
    let file = std::fs::File::open("./b.json").unwrap();
    let reader = std::io::BufReader::new(file);
    let wade_map: HashMap<String, String> = serde_json::from_reader(reader).unwrap();
    wade_map
}

pub fn converter(input: &str) -> String {
    let word_2_pin_yin = PIN_YIN_MAP.get_or_init(|| init_pin_yin_map());
    let phrase_2_pin_yin = PHRASE_PIN_YIN_MAP.get_or_init(|| init_large_pin_yin_map());
    let wade_map = WADE_MAP.get_or_init(|| init_wade_map());
    let t2s_map = T2S_MAP.get_or_init(|| init_t2s_map());
    let mut res = String::new();

    let input = input
        .chars()
        .map(|e| {
            if let Ok(n) = t2s_map.binary_search_by(|(t, _)| t.cmp(&e)) {
                t2s_map.get(n).unwrap().1
            } else {
                e
            }
        })
        .collect::<String>();
    // 没有分词
    match phrase_2_pin_yin.binary_search_by(|e| e.0.cmp(&input)) {
        Ok(n) => {
            let _s = &phrase_2_pin_yin.get(n).unwrap().1;
            let _a = _s.split(' ').map(|e| { wade_map.get(e).unwrap() });
            for e in _a {
                res.push_str(&e);
                res.push(' ');
            }
        }
        Err(_) => input.chars().for_each(|e| {
            if let Ok(n) = word_2_pin_yin.binary_search_by(|(c, _)| c.cmp(&e)) {
                if let Some(r) = wade_map.get(&word_2_pin_yin[n].1) {
                    res.push_str(r);
                    res.push(' ');
                }
            };
        }),
    }
    if !res.is_empty() {
        res.pop();
        let s = res.chars().next().unwrap();
        res.replace_range(..1, &s.to_uppercase().to_string());
    }
    res
}
